var suiteModTests = 

(function () 
{
  var module = new TestSuite("modTests"); 
  
  module.test1a =
    function ()
    {
      var src = "function F(x) {this.x=x};function G(y) {this.y=y};G.prototype.fun=function(p) {return p.x};var f=new F(123); var g=new G(456); g.fun(f);";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toNode();
      var fas = T1(fun, jsa.accesses);
      assertEquals(1, atfd(fas));
      assertEquals(0, laa(fas));
      assertEquals(1, fdp(fas));
    }
  
  module.test1b =
    function ()
    {
      var src = "function F(x) {this.x=x;}; function G(y) {this.y = y;}; G.prototype.fun=function(p) {return p.x + p.x}; var f = new F(123); var g = new G(456); g.fun(f);";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toNode();
      var fas = T1(fun, jsa.accesses);
      assertEquals(1, atfd(fas));
      assertEquals(0, laa(fas));
      assertEquals(1, fdp(fas));
    }
  
  module.test1c =
    function ()
    {
      var src = "function F(x) {this.x=x}; function G(y) {this.y=y}; function H(z) {this.z=z}; G.prototype.fun=function(p,q) {return p.x + q.z}; var f=new F(123); var g=new G(456); var h=new H(789); g.fun(f,h);";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toNode();
      var fas = T1(fun, jsa.accesses);
      assertEquals(2, atfd(fas));
      assertEquals(0, laa(fas));
      assertEquals(2, fdp(fas));
    }
  
  module.test1d =
    function ()
    {
      var src = "function F(x) {this.x=x}; function G(x) {this.x=x}; function H(x) {this.x=x}; G.prototype.fun=function(p,q) {return p.x + q.x}; var f=new F(123); var g=new G(456); var h=new H(789); g.fun(f,h);";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toNode();
      var fas = T1(fun, jsa.accesses);
      assertEquals(2, atfd(fas));
      assertEquals(0, laa(fas));
      assertEquals(2, fdp(fas));
    }
  
  module.test1e =
    function ()
    {
      var src = "function F(x,xx) {this.x=x;this.xx=xx}; function G(y) {this.y=y}; function H(z) {this.z=z}; G.prototype.fun=function(p,q) {return p.x*p.x*p.xx+q.z}; var f=new F(123,321); var g=new G(456); var h=new H(789); g.fun(f,h);";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toNode();
      var fas = T1(fun, jsa.accesses);
      assertEquals(3, atfd(fas));
      assertEquals(0, laa(fas));
      assertEquals(2, fdp(fas));
    }
  
  module.test1f =
    function ()
    {
      var src = "function F(x) {this.x=x;};F.prototype.fun=function() {return this.x};var f = new F(123); f.fun();";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toNode();
      var fas = T1(fun, jsa.accesses);
      assertEquals(0, atfd(fas));
      assertEquals(1, laa(fas));
      assertEquals(0, fdp(fas));
    }
  
  module.test1g =
    function ()
    {
      var src = "function F(x) {this.x=x;};F.prototype.fun=function() {return this.x*this.x};var f = new F(123); f.fun();";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toNode();
      var fas = T1(fun, jsa.accesses);
      assertEquals(0, atfd(fas));
      assertEquals(1, laa(fas));
      assertEquals(0, fdp(fas));
    }
  
  module.test1h =
    function ()
    {
      var src = "function S() {};S.prototype.s=666;function F(x) {this.x=x};F.prototype=Object.create(S.prototype);F.prototype.fun=function() {return this.s};var f=new F(123);f.fun()";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toNode();
      var fas = T1(fun, jsa.accesses);
      assertEquals(0, atfd(fas));
      assertEquals(1, laa(fas));
      assertEquals(0, fdp(fas));
    }
  
  module.test1i =
    function ()
    {
      var src = "function S() {};S.prototype.s=666;function F() {};F.prototype=Object.create(S.prototype);F.prototype.fun=function(p) {return p.s};var f=new F();var s=new S();f.fun(s)";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toNode();
      var fas = T1(fun, jsa.accesses);
      assertEquals(0, atfd(fas));
      assertEquals(1, laa(fas));
      assertEquals(0, fdp(fas));
    }
  
  module.test1j =
    function ()
    {
      var src = "function F(x) {this.x=x};function fun(p) {return this.x*p.y};F.prototype.fun=fun;function G(y) {this.y=y};var f=new F(123);var g=new G(456);f.fun(g)";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionDeclarationsWithName("fun").toNode();
      var fas = T1(fun, jsa.accesses);
      assertEquals(1, atfd(fas));
      assertEquals(0.5, laa(fas));
      assertEquals(1, fdp(fas));
    }
  
  module.test1k =
    function ()
    {
      var src = "function F(x){this.x=x};var A={};A.g=function(p){return p.x};var f=new F(123);A.g(f)";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toNode();
      var fas = T1(fun, jsa.accesses);
      assertEquals(1, atfd(fas));
      assertEquals(0, laa(fas));
      assertEquals(1, fdp(fas));
    }
  
  module.test1l =
    function ()
    {
      var src = "function F(x){this.x=x};F.prototype.fun=function(p){return this.x+p.y};var a={y:456};var f=new F(123);f.fun(a)";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toNode();
      var fas = T1(fun, jsa.accesses);
      assertEquals(0, atfd(fas)); // T2 => 1
      assertEquals(2, atld(fas)); // T2 => 1
      assertEquals(1, laa(fas));
      assertEquals(0, fdp(fas));
    }
  
  module.test2a =
    function ()
    {
      var src = "function F(x){this.x=x};F.prototype.fun=function(p){return this.x+p.y};var a={y:456};var f=new F(123);f.fun(a)";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toNode();
      var fas = T2(fun, jsa.accesses, ast, jsa.primLattice);
      assertEquals(1, atfd(fas)); // T1 => 2
      assertEquals(1, atld(fas)); // T1 => 0
    }
  
  module.test2b =
    function ()
    {
      var src = "function F(x){this.x=x};function fun(p){return p.x};var f=new F(123);fun(f)";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionDeclarationsWithName("fun").toNode();
      var fas = T2(fun, jsa.accesses, ast, jsa.primLattice);
      assertEquals(1, atfd(fas));
      assertEquals(0, atld(fas));
    }
  
  module.test3a =
    function ()
    {
      var src = "var o1={x:123};var o2={x:456};function fun(p){return p.x};fun(o1)+fun(o2)";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var f = $$$(ast).functionDeclarationsWithName("fun").toNode();
      var fas = T3(f, jsa.accesses, ast, jsa.primLattice);
      assertEquals(1, atfd(fas));
      assertEquals(0, atld(fas));
      assertEquals(1, fdp(fas));
    }
  
  module.test3b =
    function ()
    {
      var src = "var o1={x:123,y:111};var o2={x:456,z:222};function f(p){return p.y?p.y:p.z};f(o1)+f(o2)";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var f = $$$(ast).functionDeclarationsWithName("f").toNode();
      var fas = T3(f, jsa.accesses, ast, jsa.primLattice);
      assertEquals(2, atfd(fas));
      assertEquals(0, atld(fas));
      assertEquals(1, fdp(fas));
    }
  
  module.test3c =
    function ()
    {
      var src = "var o1={x:123,y:111};var o2={x:456,z:222};function f(p){return p.x};f(o1)+f(o2)";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var f = $$$(ast).functionDeclarationsWithName("f").toNode();
      var fas = T3(f, jsa.accesses, ast, jsa.primLattice);
      assertEquals(1, atfd(fas));
      assertEquals(0, atld(fas));
      assertEquals(1, fdp(fas));
    }
  
  module.test4a =
    function ()
    {
      var src = "function A(x){this.x=x};function B(x){this.x=x};function f(p){return p.x};var a=new A(123);var b=new B(456);f(a)+f(b)";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var f = $$$(ast).functionDeclarationsWithName("f").toNode();
      var fas = T2(f, jsa.accesses, ast, jsa.primLattice);
      assertEquals(2, atfd(fas)); 
      assertEquals(0, atld(fas));
      assertEquals(2, fdp(fas));
    }
  
  module.test4b =
    function ()
    {
      var src = "function A(x){this.x=x};function B(x){this.x=x};function f(p){return p.x};var a=new A(123);var b=new B(456);f(a)+f(b)";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var f = $$$(ast).functionDeclarationsWithName("f").toNode();
      var fas = T3(f, jsa.accesses, ast, jsa.primLattice);
      assertEquals(2, atfd(fas)); // T3 does not erase explicitly constructed types 
      assertEquals(0, atld(fas));
      assertEquals(2, fdp(fas)); // A and B are not considered to be related
    }
  
  module.test5 =
    function ()
    {
      var src = "function A(x){this.x=x};function B(x){this.x=x};B.prototype=Object.create(A.prototype);function f(p){return p.x};var a=new A(123);var b=new B(456);f(a)+f(b)";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var f = $$$(ast).functionDeclarationsWithName("f").toNode();
      var fas = T1(f, jsa.accesses);
      assertEquals(2, atfd(fas));
      assertEquals(0, atld(fas));
      assertEquals(2, fdp(fas)); // even though B < A, 2 fdp's (problem: [normally] all objects are implicitly related through Object.prototype) 
      // could solve this by stating that the link should be a type with source representation
    }
  
  module.test6a =
    function ()
    {
      var src = "function A(x){this.x=x};function B(x){this.x=x};B.prototype=Object.create(A.prototype);B.prototype.f=function (p){return p.x};var a=new A(123);var b=new B(456);b.f(a)+b.f(b)";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var f = $$$(ast).functionExpressions().toNode();
      var fas = T1(f, jsa.accesses);
      assertEquals(0, atfd(fas));
      assertEquals(2, atld(fas)); // even though B < A, 2 lda's
      assertEquals(0, fdp(fas));
    }
  
  module.test6b =
    function ()
    {
      var src = "function A(x){this.x=x};function B(x){this.x=x};B.prototype=Object.create(A.prototype);B.prototype.f=function (p){return p.x};var a=new A(123);var b=new B(456);b.f(a)+b.f(b)";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var f = $$$(ast).functionExpressions().toNode();
      var fas = T3(f, jsa.accesses, ast, jsa.primLattice);
      assertEquals(0, atfd(fas)); // T3 does not erase explicitly constructed types
      assertEquals(2, atld(fas));
      assertEquals(0, fdp(fas));
    }
  
  module.test7a = // 1a with call
    function ()
    {
      var src = "function F(x) {this.x=function () {return x}};function G(y) {this.y=function () {return y}};G.prototype.fun=function(p) {return p.x()};var f=new F(123); var g=new G(456); g.fun(f);";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toArray()[2];
      var fas = T1(fun, jsa.accesses);
      assertEquals(1, atfd(fas));
      assertEquals(0, laa(fas));
      assertEquals(1, fdp(fas));
    }
  
  module.test7b = // 1c with call
    function ()
    {
      var src = "function F(x) {this.x=function () {return x}}; function G(y) {this.y=function () {return y}}; function H(z) {this.z=function() {return z}}; G.prototype.fun=function(p,q) {return p.x() + q.z()}; var f=new F(123); var g=new G(456); var h=new H(789); g.fun(f,h);";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toArray()[3];
      var fas = T1(fun, jsa.accesses);
      assertEquals(2, atfd(fas));
      assertEquals(0, laa(fas));
      assertEquals(2, fdp(fas));
    }
  
  module.test7c = // 1f with call
    function ()
    {
      var src = "function F(x) {this.x=function() {return x}};F.prototype.fun=function() {return this.x()};var f = new F(123); f.fun();";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionExpressions().toArray()[1];
      var fas = T1(fun, jsa.accesses);
      assertEquals(0, atfd(fas));
      assertEquals(1, laa(fas));
      assertEquals(0, fdp(fas));
    }
  
  module.test8a =
    function ()
    {
      var src = "var a=[1,2,3];function f(p) {return p[0]+p[1]};f(a);";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionDeclarationsWithName("f").toNode();
      var fas = T1(fun, jsa.accesses);
      assertEquals(2, atfd(fas));
      assertEquals(0, laa(fas));
      assertEquals(1, fdp(fas));
    }
  
  module.test8b =
    function ()
    {
      var src = "var a=[1,2,3];function f(p) {return p[0]+p[1]};f(a);";
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var fun = $$$(ast).functionDeclarationsWithName("f").toNode();
      var fas = T2(fun, jsa.accesses, ast, jsa.primLattice);
      assertEquals(0, atfd(fas));
      assertNaN(laa(fas));
      assertEquals(0, fdp(fas));
    }
  
  
  // why is stack pattern correct? because nothing comes in between pushing member1Cont on stack and
  // invoking lookupAval
    
  module.TODOtest200 =
    function ()
    {
      var src = read("resources/p1.js");
      var ast = createAst(src);
      var jsa = new ModAnalysis(ast);
      var tas = typeAccesses(jsa.accesses, instanceOf1);
      var funArea = $$$(ast).functionDeclarationsWithName("area").toNode();    
      var area = jsa.analyzeFunction(funArea);
    }
  
  return module;
})();

function paperExamples()
{  
  function coverage(ast, printIt)
  {
    if (printIt)
    {
      printTree(ast);
    }
    return nodes(ast).flatMap(function (n) {return n.visited ? [] : [n.tag]});
  }
  
  var log = [];
  
  function dump(name, t, ast)
  {
    print(name);
    print(t.map(function (fa)
      {
        var baseIof = fa.baseIof;
        if (baseIof instanceof ContextAddr)
        {
          var node = addrToNode(ast)(baseIof);
        }
        else
        {
          var node = null;
        }
        return fa.toString() + " " + node;
      }).join("\n"));
    var vatfd = atfd(t);
    var vlaa = laa(t);
    var vfdp = fdp(t);
    if (vatfd > 2 && vlaa < 1/3 && vfdp <= 2)
    {
      print("FEATURE ENVY");
    }
    print(name, "atfd", vatfd, "laa", vlaa, "fdp", vfdp, "l", ldpSet(t), "f", fdpSet(t));   
    if (name == "T5")
      {
      
      log.push([vatfd, vlaa, vfdp, ldpSet(t), fdpSet(t)]);
      }
  }
  
  
  function run(name)
  {
    print("******************************************************");
    print(name);
    print("******************************************************");
    var src = read("resources/" + name);
    //var src = "function F(x) {this.x=x};function fun(p) {return this.x*p.y};F.prototype.fun=fun;function G(y) {this.y=y};var f=new F(123);var g=new G(456);f.fun(g)";
    var ast = createAst(src, {loc:true});
//    printTree(ast);
    var jsa = new ModAnalysis(ast);
//    print(coverage(ast).map(tagToNode(ast)).map(function(n){return [n,n.loc.start.line,n.loc.start.column]}).join("\n"))  
//    assertSetEquals([], coverage(ast));
    var fs = nodes(ast).filter(function (n) { return isFunctionDeclaration(n) || isFunctionExpression(n)});
    print(jsa.accesses.join("\n"));
    fs.forEach(function (f)
    {
      print("---");
      print(f.loc.start.line, f.loc.start.column, f.toString().substring(0, 60));
      if (filterFunction(f, jsa.accesses).length === 0)
      { 
        print ("no access to data");
        Ast.toplevelNodes(children(f)).filter(isMemberExpression).forEach(function (n) {print("***", n)});
      }
      else
      {
        var t1 = T1(f, jsa.accesses);
        dump("T1", t1, ast);
        var t2 = T2(f, jsa.accesses);
        dump("T2", t2, ast);
        var t3 = T3(f, jsa.accesses, ast);
        dump("T3", t3, ast);
        var t4 = T4(f, jsa.accesses, ast, jsa.primLattice);
        dump("T4", t4, ast);
        var t5 = T5(f, jsa.accesses, ast, jsa.primLattice);
        dump("T5", t5, ast);
      }
    })
  }
  
//print(coverage(ast).map(tagToNode(ast)).map(function(n){return [n,n.loc.start.line,n.loc.start.column]}).join("\n"))  
  
// run("navier-stokes.js");
 //run("richards.js");

//  run("p0.js");
//  run("p1.js");
//  run("p2.js");
//  run("p2b.js");
//  run("p3.js");
//  run("p4.js");

//  run("p11.js");
//  run("p12.js");
//  run("p13.js");
//  print(log.join("\n"));
  
  run("loopy.js");
}
